package com.example.opengl_test00;

import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas.VertexMode;
import android.opengl.GLUtils;
import android.util.Log;

public class Sphere extends Polygon {
	
	/**
	 * Number of S axis vertex
	 */
	private int mSVertexs;
	/**
	 * Number of T axis vertex
	 */
	private int mTVertexs;
	
	private int mTextureNumber;
	
	public Sphere(float radius, int division) {
		mTextureNumber = 0;
		
		int _d = division + 1;
		float angle = (float)(Math.PI / 2.0) / _d;
		mSVertexs = _d * 4 + 1;
		mTVertexs = _d * 2 + 1;
		
		// vertex buffer
		mNumberOfVertexes = mSVertexs * mTVertexs;
		mVertexBuf = createFloatBuffer(4 * 3 * mNumberOfVertexes);
		for (int j = 0; j < mTVertexs; j++) {
			float y = radius * (float)Math.cos(angle * j);
			float w = radius * (float)Math.sin(angle * j);
			for (int i = 0; i < mSVertexs; i++) {
				mVertexBuf.put(w * (float)Math.cos(angle * i));
				mVertexBuf.put(y);
				mVertexBuf.put(w * (float)Math.sin(angle * i));
			}
		}
		mVertexBuf.position(0);
		
		// texture buffer
		mTextureBuf = createFloatBuffer(4 * 3 * mNumberOfVertexes);
		for (int y = 0; y < mTVertexs; y++) {
			float t = (float)y / (float)mTVertexs;
			for (int x = mSVertexs-1; x >= 0 ; x--) {
				mTextureBuf.put((float)x / (float)mSVertexs);
				mTextureBuf.put(t);
			}
		}
		mTextureBuf.position(0);
		
		// index buffer.
		int numberOfTriangles = (mTVertexs - 1) * (mSVertexs - 1) * 2;
		mIndexBuf = createShortBuffer(numberOfTriangles * 3 * 2);
		for (short k = 0; k < mTVertexs - 1; k++) {
			for (short i = 0; i < mSVertexs - 1; i++) {
				short p1 = (short) (i + k * mSVertexs);
				for (int j = 0; j < 2; j++) {
					if (j == 0)	{
						mIndexBuf.put(p1);
						mIndexBuf.put((short) (p1 + mSVertexs + 1));
						mIndexBuf.put((short) (p1 + mSVertexs));
					} else {
						mIndexBuf.put(p1);
						mIndexBuf.put((short) (p1 + 1));
						mIndexBuf.put((short) (p1 + mSVertexs + 1));
					}
				}
			}
		}
		mNumberOfIndexes = mIndexBuf.position();
		mIndexBuf.position(0);
		
		// normal buffer
//		ArrayList<Vertor3f> vertorBuf = new ArrayList<Vertor3f>();
		mNormalBuf = createFloatBuffer(4 * 3 * mNumberOfVertexes);
		// vertex normal
		for (int i = 0; i < mNumberOfVertexes * 3; i += 3) {
			Vertor3f v = new Vertor3f(
					mVertexBuf.get(i + 0),
					mVertexBuf.get(i + 1),
					mVertexBuf.get(i + 2));
			mNormalBuf.put(v.normal().toArray());	// must to be normal
		}
//		for (int i = 0; i < mNumberOfIndexes; i++)
//		{
//			int index = mIndexBuf.get(i);
//			vertorBuf.add(new Vertor3f(
//					mVertexBuf.get(index + 0),
//					mVertexBuf.get(index + 1),
//					mVertexBuf.get(index + 2)
//			));
//			
//			if (i % 3 == 2)
//			{
//				Vertor3f normal = vertorBuf.get(0).sub(vertorBuf.get(1)).cross(
//						vertorBuf.get(0).sub(vertorBuf.get(2)));
//				
//				mNormalBuf.put(normal.toArray());
//				vertorBuf.clear();
//			}
//		}
		mNormalBuf.position(0);
	}
	
	@Override
	public void createTexture(GL10 gl, InputStream texture) {
		Bitmap bitmap = BitmapFactory.decodeStream(texture);
		//
		gl.glEnable(GL10.GL_TEXTURE_2D);
		
		// create texture object
		int[] textureNumber = new int[1];
		gl.glGenTextures(1, textureNumber, 0);
		mTextureNumber = textureNumber[0];
		
		// bind texture object
		gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureNumber);
		
		// set texture image
		GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
		bitmap.recycle();
		
		// set texture parameter
		gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT );
		gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT );
		gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR );
		gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR );
	}
	
	private FloatBuffer mMaterialEmission = FloatBuffer.wrap(new float[] { 0.0f, 0.0f, 0.0f, 1.0f } );
	private FloatBuffer mMaterialAmbient = FloatBuffer.wrap(new float[] { 0.1f, 0.1f, 0.1f, 1.0f } );
	private FloatBuffer mMaterialDiffuse = FloatBuffer.wrap(new float[] { 0.4f, 0.4f, 1.0f, 1.0f } );
	private FloatBuffer mMaterialSpecular = FloatBuffer.wrap(new float[] { 1.0f, 1.0f, 1.0f, 1.0f } );
	
	@Override
	public void draw(GL10 gl) {
		
		// set vertex buffer
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuf);
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		
		// set normal buffer
		gl.glNormalPointer(GL10.GL_FLOAT, 0, mNormalBuf);
		gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
		
		// set texture buffer
		if (mTextureNumber != 0)
		{
			gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuf);
			gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
			gl.glActiveTexture(GL10.GL_TEXTURE0);
		}
		
		Log.d("Sphere", "mMaterialDiffuse = { " + mMaterialDiffuse.get(0) +", "+ mMaterialDiffuse.get(1) +", "+ mMaterialDiffuse.get(2)+", "+ mMaterialDiffuse.get(3) + " }");
		
		gl.glEnable(GL10.GL_COLOR_MATERIAL);
		gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_EMISSION, mMaterialEmission);
		gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_AMBIENT, mMaterialAmbient);
		gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_DIFFUSE, mMaterialDiffuse);
		gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_SPECULAR, mMaterialSpecular);
		gl.glMaterialf(GL10.GL_FRONT, GL10.GL_SHININESS, 60.0f);
		
		// draw
		gl.glDrawElements(GL10.GL_TRIANGLES, mNumberOfIndexes, GL10.GL_UNSIGNED_SHORT, mIndexBuf);
		
		// reset client state
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
	}
}
